package com.yonyou.pmclouds.reason.model;

import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.util.CodeNameUtils;
import com.yonyou.pmclouds.reason.entity.ProblemReasonVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 问题原因vo管理器
 *
 * @author th
 * @date 2019/10/22 10:29
 */
@Component
public class ProblemReasonManager {

    /**
     * 从二维列表解析问题原因，默认第一行为标题行
     * 从第二行开始解析，每行解析前3列
     *
     * @param data 二维列表数据
     * @return 根问题原因(树)列表
     */
    public List<ProblemReasonVO> generateProbReasonFromExcelData(List<List<String>> data) throws BusinessException {
        List<ProblemReasonVO> probReasonList = null;
        if (CollectionUtils.isEmpty(data)) {
            return probReasonList;
        }
        // 准备返回的，根问题原因(树)的列表
        probReasonList = new ArrayList<>();
        // 当前操作的行对应的根、二级问题原因对象
        ProblemReasonVO root = null, second = null;
        // 当前操作的行对应的根、二级问题原因对象的子问题原因的列表
        List<ProblemReasonVO> rootChild = null, secondChild = null;
        // 同级问题原因的名称集合
        Set<String> rootNameSet = new HashSet<>(), secondNameSet = new HashSet<>(), thirdNameSet = new HashSet<>();

        // 从第二行开始遍历
        for (int i = 1; i < data.size(); i++) {
            List<String> dataRow = data.get(i);
            // 处理根问题原因
            String rootProbReasonName = dataRow.get(0);
            if (StringUtils.isEmpty(rootProbReasonName)) {
                // 缺少根问题原因
                if (root == null)
                    return probReasonList;
            } else if (root != null && root.getSname().equals(rootProbReasonName)) {
                // 还是之前的根问题原因，无需处理
            } else {// 新根问题原因
                if (!checkName(rootProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行一级问题原因名称【" + rootProbReasonName + "】不合规范，请检查");
                }

                if (rootNameSet.contains(rootProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行一级问题原因名称【" + rootProbReasonName + "】重复，请检查");
                } else {
                    rootNameSet.add(rootProbReasonName);
                }

                // 之前存在根问题原因并且有子问题原因
                if (root != null && CollectionUtils.isNotEmpty(rootChild)) {
                    root.setChildReason(rootChild.toArray(new ProblemReasonVO[0]));
                }
                // 存在二级问题原因并且有子问题原因
                if (second != null && CollectionUtils.isNotEmpty(secondChild)) {
                    second.setChildReason(secondChild.toArray(new ProblemReasonVO[0]));
                    second = null;
                }

                secondNameSet.clear();
                thirdNameSet.clear();

                root = new ProblemReasonVO();
                rootChild = new ArrayList<>();
                root.setSname(rootProbReasonName);
                probReasonList.add(root);
            }

            // 处理二级问题原因
            String secondProbReasonName = dataRow.get(1);
            if (StringUtils.isEmpty(secondProbReasonName)) {
                // 检查当前是否有二级问题原因信息
                if (second == null) {
                    // 有三级或四级问题原因
                    if (StringUtils.isNotEmpty(dataRow.get(2))) {
                        throw new BusinessException("Excel表格第" + (i + 1) + "行，空二级问题原因下存在下级问题原因，请检查");
                    } else {// 此行只有一级问题原因
                        continue;
                    }
                }
            } else if (second != null && second.getSname().equals(secondProbReasonName)) {
                // 还是之前的二级问题原因，无需处理
            } else {// 新二级问题原因
                if (!checkName(secondProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行二级问题原因名称【" + secondProbReasonName + "】不合规范，请检查");
                }

                if (secondNameSet.contains(secondProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行二级问题原因名称【" + secondProbReasonName + "】重复，请检查");
                } else {
                    secondNameSet.add(secondProbReasonName);
                }

                // 如果之前存在二级问题原因并且有子问题原因
                if (second != null && CollectionUtils.isNotEmpty(secondChild)) {
                    second.setChildReason(secondChild.toArray(new ProblemReasonVO[0]));
                }

                thirdNameSet.clear();

                second = new ProblemReasonVO();
                secondChild = new ArrayList<>();
                second.setSname(secondProbReasonName);
                rootChild.add(second);
            }

            // 处理三级问题原因
            String thirdProbReasonName = dataRow.get(2);
            if (StringUtils.isEmpty(thirdProbReasonName)) {
                continue;// 此行只有二级问题原因
            } else {
                if (!checkName(thirdProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行三级问题原因名称【" + thirdProbReasonName + "】不合规范，请检查");
                }

                if (thirdNameSet.contains(thirdProbReasonName)) {
                    throw new BusinessException("Excel表格第" + (i + 1) + "行三级问题原因名称【" + thirdProbReasonName + "】重复，请检查");
                } else {
                    thirdNameSet.add(thirdProbReasonName);
                }

                ProblemReasonVO third = new ProblemReasonVO();
                third.setSname(thirdProbReasonName);
                secondChild.add(third);
            }
        }

        // 遍历完成后，维护最后一行各级问题原因的子问题原因关系
        if (root != null && CollectionUtils.isNotEmpty(rootChild)) {
            root.setChildReason(rootChild.toArray(new ProblemReasonVO[0]));
        }
        if (second != null && CollectionUtils.isNotEmpty(secondChild)) {
            second.setChildReason(secondChild.toArray(new ProblemReasonVO[0]));
        }

        return probReasonList;
    }

    /**
     * 检查问题原因名称长度
     *
     * @param name 问题原因名称
     * @return true 问题原因名称符合规范
     */
    private boolean checkName(String name) {
        if (CodeNameUtils.checkNameStr(name, 64) >= 0)
            return false;

        return true;
    }
}
